<template>
  <div class="header">
    <div class="city">{{ localCity }}</div>
    <el-input
      v-model="searchedCity"
      placeholder="请搜索你所在的城市"
      style="width: 240px"
      size="large"
      :prefix-icon="Search"
      @change="searchCity(searchedCity)"
    />
    <el-select
      v-model="drawType"
      placeholder="选择绘制图形"
      style="width: 240px"
      size="large"
      @change="handleDraw"
    >
      <el-option
        v-for="(item, index) in drawOptions"
        :key="index"
        :label="item.label"
        :value="item.value"
      ></el-option>
    </el-select>
    <el-select
      v-model="markerType"
      placeholder="选择标记类型"
      style="width: 240px"
      size="large"
      :disabled="zoom < 13"
      @click="handleMarkerClick"
      @change="handleMarker"
    >
      <el-option
        v-for="(item, index) in markerOptions"
        :key="index"
        :label="item.label"
        :value="item.value"
      ></el-option>
    </el-select>
  </div>
  <div id="container"></div>
  <div id="overlay" ref="overlay"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { getIpData } from '@/api'
import 'ol/ol.css'
import { Map, View, Overlay, Feature } from 'ol'
import XYZ from 'ol/source/XYZ.js'
import TileLayer from 'ol/layer/Tile.js'
import VectorLayer from 'ol/layer/Vector.js'
import VectorSource from 'ol/source/Vector.js'
import GeoJSON from 'ol/format/GeoJSON.js'
import Style from 'ol/style/Style.js'
import Stroke from 'ol/style/Stroke.js'
import Fill from 'ol/style/Fill.js'
import Text from 'ol/style/Text.js'
import Icon from 'ol/style/Icon.js'
import CircleStyle from 'ol/style/Circle.js'
import Draw from 'ol/interaction/Draw.js'
import Point from 'ol/geom/Point.js'
import Cluster from 'ol/source/Cluster.js'
import { getLength } from 'ol/sphere'
import { easeOut } from 'ol/easing'
import AnimatedClusterLayer from 'ol-ext/layer/AnimatedCluster.js'

const localCity = ref('加载中...')
const searchedCity = ref('')
const drawType = ref('')
const markerType = ref('')
const zoom = ref(4)
const overlay = ref(null)
let overlayLayer = null

const drawOptions = ref([
  { label: '直线', value: 'LineString' },
  { label: '圆形', value: 'Circle' },
  { label: '多边形', value: 'Polygon' },
  { label: '自由画笔', value: 'FreeDraw' },
  { label: '清除绘图', value: 'Clear' },
])
const markerOptions = ref([
  { label: '充电站', value: 'charge' },
  { label: '公交站', value: 'bus' },
  { label: '停车场', value: 'park' },
  { label: '退出标记', value: 'exitMarker' },
])

const view = new View({
  projection: 'EPSG:4326',
  center: [114.4, 30.59],
  zoom: 4,
})

// 高德地图底图
const baseMapLayer = new TileLayer({
  source: new XYZ({
    url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}',
    wrapX: false,
  }),
})

// 城市/省份高亮图层
const areaHighlightStyle = new Style({
  fill: new Fill({ color: 'rgba(255, 0, 0, 0.4)' }),
  stroke: new Stroke({ color: '#000', width: 2 }),
})
const provinceDataLayer = new VectorLayer({
  source: new VectorSource({
    url: 'https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json',
    format: new GeoJSON(),
  }),
  style: new Style({}),
})
const cityDataLayer = new VectorLayer({
  source: new VectorSource({
    url: 'https://geo.datav.aliyun.com/areas_v3/bound/100000_full_city.json',
    format: new GeoJSON(),
  }),
  style: new Style({}),
})

// 绘制
const drawSourceLayer = new VectorSource()
const drawFinalStyle = new Style({
  fill: new Fill({ color: 'rgba(255, 0, 0, 0.4)' }),
  stroke: new Stroke({ color: '#000', width: 2 }),
})
// 绘制图层
const drawResultLayer = new VectorLayer({
  source: drawSourceLayer,
  style: drawFinalStyle,
  visible: false,
})

// 标记聚合
const markerSourceLayer = new VectorSource()
const clusterSourceLayer = new Cluster({
  source: markerSourceLayer,
  distance: 80,
})

const animatedClusterLayer = new AnimatedClusterLayer({
  source: clusterSourceLayer,
  animationDuration: 300,
  style: (clusterFeature) => {
    const features = clusterFeature.get('features')
    const size = features.length
    if (size > 1) {
      return new Style({
        image: new CircleStyle({
          radius: 15 + Math.min(Math.floor(size / 10), 10),
          fill: new Fill({ color: '#4798cc' }),
          stroke: new Stroke({ color: '#fff', width: 2 }),
        }),
        text: new Text({
          text: size.toString(),
          font: '16px sans-serif',
          fill: new Fill({ color: 'white' }),
        }),
      })
    } else {
      const markerType = features[0].get('markerType')
      return new Style({
        image: new Icon({
          src: `/public/${markerType}.svg`,
          width: 30,
          height: 30,
          anchor: [0.5, 1],
        }),
      })
    }
  },
})

// 初始化地图
const map = new Map({
  view: view,
  layers: [
    baseMapLayer,
    provinceDataLayer,
    cityDataLayer,
    drawResultLayer,
    animatedClusterLayer,
  ],
})

onMounted(async () => {
  map.setTarget('container')
  // 初始化弹窗
  overlayLayer = new Overlay({
    element: overlay.value,
    position: undefined,
    offset: [10, 10],
  })
  map.addOverlay(overlayLayer)
  await initMapData()

  // 监听缩放变化
  view.on('change:resolution', () => {
    zoom.value = view.getZoom()
    handleZoomUpdate()
  })

  map.on('click', showAreaoverlay)
})

async function initMapData() {
  const ipData = await getIpData('e05b2f8dddd45367d027e8f1792cc710')
  localCity.value = ipData.city

  let provinceLoaded = false
  let cityLoaded = false

  provinceDataLayer.getSource().once('featuresloadend', () => {
    provinceLoaded = true
    highlightCurrentArea(ipData.city)
  })
  cityDataLayer.getSource().once('featuresloadend', () => {
    cityLoaded = true
    highlightCurrentArea(ipData.city)
  })

  map.once('rendercomplete', () => {
    const targetFeature = findAreaFeature(
      cityDataLayer.getSource().getFeatures(),
      ipData.city
    )
    if (targetFeature) {
      const center = targetFeature.get('center')
      view.animate({
        center: center,
        zoom: 8,
        duration: 2000,
        easing: easeOut,
      })
    }
  })
}

async function searchCity(cityName) {
  if (!cityName) return

  let targetFeature = null
  const cityFeatures = cityDataLayer.getSource().getFeatures()
  const provinceFeatures = provinceDataLayer.getSource().getFeatures()

  targetFeature = findAreaFeature(cityFeatures, cityName)
  if (!targetFeature) {
    targetFeature = findAreaFeature(provinceFeatures, cityName)
  }

  if (!targetFeature) {
    ElMessage.error(`未找到地区: ${cityName}`)
    searchedCity.value = ''
    return
  }

  // 高亮并缩放至目标区域
  highlightTargetFeature(targetFeature)
  const center = targetFeature.get('center')
  const targetZoom = targetFeature.get('level') === 'province' ? 6 : 8

  view.animate({
    center: center,
    zoom: targetZoom,
    duration: 2000,
    easing: easeOut,
  })

  searchedCity.value = ''
  overlayLayer.setPosition(undefined)
}

// 查找区域
function findAreaFeature(features, searchName) {
  for (const feature of features) {
    const name = feature.get('name') || ''
    const cleanName = name.replace(/市|省|自治区|特别行政区/g, '')

    if (
      name === searchName ||
      name === `${searchName}市` ||
      name === `${searchName}省` ||
      cleanName === searchName ||
      name.includes(searchName)
    ) {
      return feature
    }
  }
  return null
}

// 高亮
function highlightTargetFeature(feature) {
  // 清除之前的高亮
  provinceDataLayer
    .getSource()
    .getFeatures()
    .forEach((f) => f.setStyle(null))
  cityDataLayer
    .getSource()
    .getFeatures()
    .forEach((f) => f.setStyle(null))

  // 高亮当前要素
  feature.setStyle(areaHighlightStyle)
}

// 初始化区域高亮
function highlightCurrentArea(cityName) {
  const cityFeatures = cityDataLayer.getSource().getFeatures()
  const targetFeature = findAreaFeature(cityFeatures, cityName)
  if (targetFeature) {
    highlightTargetFeature(targetFeature)
  }
}

let drawInteraction = null 

function handleDraw() {
  // 重置状态
  markerType.value = ''
  map.un('click', addMarkerPoint)
  overlayLayer.setPosition(undefined)
  // 移除之前的绘制交互
  if (drawInteraction) {
    map.removeInteraction(drawInteraction)
    drawInteraction = null
  }
  // 清除绘制
  if (drawType.value === 'Clear') {
    drawSourceLayer.clear()
    drawResultLayer.setVisible(false)
    drawType.value = ''
    return
  }

  drawResultLayer.setVisible(true)
  createDrawInteraction(drawType.value)
}

function createDrawInteraction(type) {
  let freehand = false
  if (type === 'FreeDraw') {
    freehand = true
    type = 'LineString'
  }
  drawInteraction = new Draw({
    source: drawSourceLayer,
    type: type,
    freehand: freehand,
    stopEvent: false,
  })

  map.addInteraction(drawInteraction)
  drawInteraction.setActive(true)

  // 显示长度
  drawInteraction.on('drawstart', (e) => {
    const geometry = e.feature.getGeometry()
    geometry.on('change', () => {
      if (drawType.value === 'LineString') {
        const lastCoord = geometry.getLastCoordinate()
        const length = getLength(geometry, { projection: 'EPSG:4326' }) / 1000
        overlayLayer.setPosition(lastCoord)
        overlay.value.innerHTML = `<p>直线长度: ${length.toFixed(2)}km</p>`
      }
    })
  })
  drawInteraction.on('drawend', (e) => {
    e.feature.setStyle(drawFinalStyle)
    overlayLayer.setPosition(undefined)
  })
}

function handleMarkerClick() {
  if (zoom.value < 13) {
    ElMessage.error({ message: '请放大到13级以上', duration: 1000 })
  }
}

function handleMarker() {
  // 重置状态
  drawType.value = ''
  if (drawInteraction) {
    map.removeInteraction(drawInteraction)
    drawInteraction = null
  }
  drawResultLayer.setVisible(false)

  // 解绑之前的标记事件
  map.un('click', addMarkerPoint)

  // 退出标记模式
  if (markerType.value === 'exitMarker') {
    markerType.value = ''
    ElMessage.success('退出标记模式')
    return
  }

  if (zoom.value < 13) {
    ElMessage.error('请放大到13级以上进行标记')
    markerType.value = ''
    return
  }

  // 绑定标记事件
  map.on('click', addMarkerPoint)
}


function addMarkerPoint(e) {
  const coordinate = e.coordinate
  const feature = new Feature({
    geometry: new Point(coordinate),
    markerType: markerType.value, 
  })
  markerSourceLayer.addFeature(feature)
}


function handleZoomUpdate() {
  // 缩放小于10级显示区域高亮，否则隐藏
  if (zoom.value < 10) {
    provinceDataLayer
      .getSource()
      .getFeatures()
      .forEach((f) => {
        if (f.getStyle()) f.setStyle(areaHighlightStyle)
      })
    cityDataLayer
      .getSource()
      .getFeatures()
      .forEach((f) => {
        if (f.getStyle()) f.setStyle(areaHighlightStyle)
      })
  } else {
    provinceDataLayer
      .getSource()
      .getFeatures()
      .forEach((f) => f.setStyle(null))
    cityDataLayer
      .getSource()
      .getFeatures()
      .forEach((f) => f.setStyle(null))
    overlayLayer.setPosition(undefined)
  }

  // 缩放小于13级时重置标记
  if (zoom.value < 13 && markerType.value !== 'exitMarker') {
    markerType.value = ''
    map.un('click', addMarkerPoint)
  }
}

// 弹窗
function showAreaoverlay(e) {
  if (drawType.value !== '' || markerType.value !== '') return

  const feature = map.forEachFeatureAtPixel(e.pixel, (f) => {
    return f.get('name') ? f : null
  })

  if (feature) {
    const name = feature.get('name')
    const center = feature.get('center') || e.coordinate
    overlayLayer.setPosition(center)
    overlay.value.innerHTML = `
      <p>地区名: ${name}</p>
      <p>经度: ${center[0].toFixed(4)}</p>
      <p>纬度: ${center[1].toFixed(4)}</p>
    `
  } else {
    overlayLayer.setPosition(undefined)
  }
}
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html,
body,
#app,
#container {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.header {
  display: flex;
  gap: 100px;
  position: fixed;
  align-items: center;
  height: 80px;
  background: #4264fb;
  width: 100%;
  z-index: 1000;
  padding: 0 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.header .city {
  margin-inline: 50px;
  color: #fff;
  font-size: 16px;
  font-weight: 500;
  white-space: nowrap;
}
#container {
  padding-top: 80px;
}
#overlay {
  min-width: 200px;
  padding: 12px 15px;
  border-radius: 8px;
  box-shadow: 0 3px 15px rgba(0, 0, 0, 0.2);
  background-color: #fff;
  border: 1px solid #ff69b4;
  font-size: 14px;
  line-height: 1.6;
}
#overlay p {
  margin: 0;
  color: #333;
}
#overlay p:not(:last-child) {
  margin-bottom: 4px;
}
</style>
